﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace IkeCode.DateTimePicker
{
    /// <summary>
    /// Interaction logic for DateTimePicker.xaml
    /// </summary>
    public partial class DateTimePicker : UserControl
    {
        public DateTimePicker()
        {
            InitializeComponent();
        }

        protected override void OnRender(DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);

            var now = DateTime.Now.AddHours(this.HourTolerance);

            var dateStartIsValid = this.CalendarDateStart > DateTime.MinValue;

            if (this.ShowOnlyFutureDays)
            {
                var dateStart = dateStartIsValid && this.CalendarDateStart.Date >= now.Date ? this.CalendarDateStart : now;
                this.Calendar.DisplayDateStart = dateStart;
            }
            else if (dateStartIsValid)
            {
                this.Calendar.DisplayDateStart = this.CalendarDateStart.Date <= this.CurrentDateTime.Date ? this.CalendarDateStart : this.CurrentDateTime;
                //this.Calendar.DisplayDateStart = this.CalendarDateStart;
                this.InternalCurrentDateTime = this.InternalCurrentDateTime == DateTime.MinValue ? this.CalendarDateStart : this.InternalCurrentDateTime;
            }

            if (this.CalendarDateEnd > DateTime.MinValue && this.CalendarDateEnd > this.CalendarDateStart)
                this.Calendar.DisplayDateEnd = this.CalendarDateEnd;

            this.InternalCurrentDateTime = this.InternalCurrentDateTime == DateTime.MinValue ? this.CurrentDateTime : this.InternalCurrentDateTime;

            this.Calendar.DisplayDate = this.CalendarDisplayDate == DateTime.MinValue ? this.CurrentDateTime : this.CalendarDisplayDate;
            this.Calendar.SelectedDate = this.CalendarSelectedDate == DateTime.MinValue ? this.CurrentDateTime : this.CalendarSelectedDate;

            this.RenderGridHours();
        }

        #region ## Properties ##

        private const int MAX_ROWS = 5; //Zero based
        private const int MAX_COLUMNS = 3; //Zero based

        private DateTime InternalCurrentDateTime { get; set; }

        #endregion

        #region ## Dependency Properties ##

        public DateTime CurrentDateTime
        {
            get { return (DateTime)GetValue(CurrentDateTimeProperty); }
            set { SetValue(CurrentDateTimeProperty, value); }
        }

        public static readonly DependencyProperty CurrentDateTimeProperty =
            DependencyProperty.Register("CurrentDateTime", typeof(DateTime), typeof(DateTimePicker), new PropertyMetadata(DateTime.Now));

        #region DateStart, DateEnd, ShowOnlyFutureDays, HourTolerance, ShowHours

        public DateTime CalendarDateStart
        {
            get { return (DateTime)GetValue(CalendarDateStartProperty); }
            set { SetValue(CalendarDateStartProperty, value); }
        }

        public static readonly DependencyProperty CalendarDateStartProperty =
            DependencyProperty.Register("CalendarDateStart", typeof(DateTime), typeof(DateTimePicker), new PropertyMetadata(CalendarDateStartChanged));

        public static void CalendarDateStartChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var picker = sender as DateTimePicker;
            var date = (DateTime)e.NewValue;
            picker.CalendarDateStart = date;
            picker.Calendar.DisplayDateStart = date;
        }

        public DateTime CalendarDateEnd
        {
            get { return (DateTime)GetValue(CalendarDateEndProperty); }
            set { SetValue(CalendarDateEndProperty, value); }
        }

        public static readonly DependencyProperty CalendarDateEndProperty =
            DependencyProperty.Register("CalendarDateEnd", typeof(DateTime), typeof(DateTimePicker), new PropertyMetadata(CalendarDateEndChanged));

        public static void CalendarDateEndChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var picker = sender as DateTimePicker;
            var date = (DateTime)e.NewValue;
            picker.CalendarDateEnd = date;
            picker.Calendar.DisplayDateEnd = date;
        }

        public DateTime CalendarDisplayDate
        {
            get { return (DateTime)GetValue(CalendarDisplayDateProperty); }
            set { SetValue(CalendarDisplayDateProperty, value); }
        }

        public static readonly DependencyProperty CalendarDisplayDateProperty =
            DependencyProperty.Register("CalendarDisplayDate", typeof(DateTime), typeof(DateTimePicker), new PropertyMetadata(CalendarDisplayDateChanged));

        public static void CalendarDisplayDateChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var picker = sender as DateTimePicker;
            var date = (DateTime)e.NewValue;
            picker.CalendarDisplayDate = date;
            picker.Calendar.DisplayDate = date;
        }

        public DateTime CalendarSelectedDate
        {
            get { return (DateTime)GetValue(CalendarSelectedDateProperty); }
            set { SetValue(CalendarSelectedDateProperty, value); }
        }

        public static readonly DependencyProperty CalendarSelectedDateProperty =
            DependencyProperty.Register("CalendarSelectedDate", typeof(DateTime), typeof(DateTimePicker), new PropertyMetadata(CalendarSelectedDateChanged));

        public static void CalendarSelectedDateChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var picker = sender as DateTimePicker;
            var date = (DateTime)e.NewValue;
            picker.CalendarSelectedDate = date;
            picker.Calendar.SelectedDate = date;
        }

        public bool ShowOnlyFutureDays
        {
            get { return (bool)GetValue(ShowOnlyFutureDaysProperty); }
            set { SetValue(ShowOnlyFutureDaysProperty, value); }
        }

        public static readonly DependencyProperty ShowOnlyFutureDaysProperty =
            DependencyProperty.Register("ShowOnlyFutureDays", typeof(bool), typeof(DateTimePicker), new PropertyMetadata(false));

        public int HourTolerance
        {
            get { return (int)GetValue(HourToleranceProperty); }
            set { SetValue(HourToleranceProperty, value); }
        }

        public static readonly DependencyProperty HourToleranceProperty =
            DependencyProperty.Register("HourTolerance", typeof(int), typeof(DateTimePicker), new PropertyMetadata(0));

        public bool ShowHours
        {
            get { return (bool)GetValue(ShowHoursProperty); }
            set { SetValue(ShowHoursProperty, value); }
        }

        public static readonly DependencyProperty ShowHoursProperty =
            DependencyProperty.Register("ShowHours", typeof(bool), typeof(DateTimePicker), new PropertyMetadata(true));

        #endregion

        #endregion

        #region ## Private Methods ##

        /// <summary>
        /// Render hours grid
        /// </summary>
        private void RenderGridHours()
        {
            if (ShowHours)
            {
                var startHour = 0;

                if (this.InternalCurrentDateTime.Date > this.CurrentDateTime.Date && this.ShowOnlyFutureDays)
                {
                    startHour = 100;
                }
                else if (this.InternalCurrentDateTime.Date == this.CurrentDateTime.Date && this.ShowOnlyFutureDays)
                {
                    startHour = this.InternalCurrentDateTime.Hour;
                }

                if (this.gridHours.Children != null)
                {
                    this.gridHours.Children.Clear();

                    int row = 0, column = 0;
                    for (int i = 0; i < 24; i++)
                    {
                        var format = i < 10 ? "0{0}:00" : "{0}:00";
                        var hour = string.Format(format, i);

                        RadioButton rb = new RadioButton();
                        rb.Content = hour;
                        rb.Tag = i;
                        rb.HorizontalAlignment = HorizontalAlignment.Stretch;
                        rb.VerticalAlignment = VerticalAlignment.Stretch;
                        rb.GroupName = "Hours";
                        rb.Checked += rb_Checked;
                        rb.IsChecked = i == startHour;
                        rb.IsEnabled = i >= startHour + this.HourTolerance;

                        Grid.SetRow(rb, row);
                        Grid.SetColumn(rb, column);

                        this.gridHours.Children.Add(rb);

                        if (column == MAX_COLUMNS)
                        {
                            row++;
                            column = 0;
                        }
                        else
                        {
                            column++;
                        }
                    }
                }
            }
        }

        #endregion

        #region ## Events ##

        private void Calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
        {
            var dates = e.AddedItems as object[];
            if (dates != null && dates.Length > 0)
                this.CurrentDateTime = (DateTime)dates[0];

            this.RenderGridHours();
        }

        protected void rb_Checked(object sender, RoutedEventArgs e)
        {
            var button = (RadioButton)e.Source;
            if (button != null)
            {
                int hour = 0;
                Int32.TryParse(button.Tag.ToString(), out hour);

                int minute = 0;
                int second = 0;

                var currentDateTime = this.CurrentDateTime;

                var newDate = new DateTime(
                        currentDateTime.Year,
                        currentDateTime.Month,
                        currentDateTime.Day,
                        hour,
                        minute,
                        second);

                this.CurrentDateTime = newDate;
            }
        }

        #endregion
    }
}
